串(string)(或字符串)是由零个或多个字符组成的有限序列,一般记为
s=‘a1a2...an’ (n>=0)
串的数据元素固定为字符型的线性表,因此串的逻辑结构和线性表极为相似,只不过对串的操作常常是以”串的整体”或“子串”作为操作对象,而线性表的操作大多以“单个数据元素”为操作对象。
静态存储分配的顺序串(定长)
类似于线性表的顺序存储结构,用一组地址连续的存储单元存储串值的字符序列。在串的定长顺序存储结构中,按照预定义的大小,为每个定义的串变量分配一个固定长度
的存储区,则可以用定长数组如下描述。
|
|
综上两个操作可知,在顺序存储结构中,实现串操作的原操作为“字符序列的复制”,操作时间复杂度基于字符序列的长度。串连接的特点是,如果在操作中出现串值序列的长度超过MAXSTRLEN时,约定用截尾法处理,这种情况不仅在串联接时发生,在串的其它操作中,如插入、置换等也可能发生。克服这个弊病唯有不限定串长的最大长度,即动态分配串值的存储空间。
###动态存储分配的顺序串(堆分配存储表示)
由于多数情况下串的操作是以串的整体或子串的形式参与,则在应用程序中,参与运算的串变量之间的长度相差较大,因此为串变量设定固定大小空间的数组不尽合理,需要根据具体情况来决定串空间的大小。
动态存储分配的顺序串完全可以用动态存储分配的顺序表SqList来表示,这样的顺序表的有关操作都可以用来处理顺序串的相关操作,如初始化、求串长等。利用malloc()函数为每个新产生的串分配一块实际串所需的存储空间,若分配成功,则返回一个指向起始地址的指针,作为串的地址。这样,当进行串的插入、连接等操作时,再根据实际需要的增补空间,以满足插入和连接等操作的需要。所以在描述动态顺序串时,当前数组的容量和增补空间不再作为结构的一部分,其结构的描述如下:
|
|
由于动态存储分配的顺序串既具有顺序存储结构的优点(随机存取,操作简单),同时串值空间的大小是在程序执行时动态分配而得,这对串的插入、连接、置换等操作非常有利,因此在串处理的应用程序中也常被选用。
这种存储结构表示的串操作仍是基于“字符序列的复制”进行的。
1.串赋值操作
串赋值操作就是把一个字符串常量赋值给顺序串S。成功赋值返回true,否则返回false。其主要操作是:①判断顺序串是否非空,若是,则释放其空间(尽管这样做也不影响操作的执行结果,但适时地进行空闲空间回收是一个好的编程习惯);②求串常量的长度,若等于0,就将顺序串S置空,否则,以此长度为标准为顺序串S申请空间;③把串常量chars的值复制到串S中去,同时顺序串S的长度被赋值为串常量的长度。
|
|
又例如串复制操作StrCopy_Sq(&T, S)的算法实现是,若串T已存在,则先释放T所占的空间,当S不空时,首先为T分配大小和S长度相等的存储空间,然后将串S复制到串T中;又如串插入操作(连接)操作StrConcat_Sq(&S,T),首先判断T的长度是否为非0,若是,则为S分配长度为T.length的增补空间,然后将T中的所有字符复制在S的后面。
关于动态顺序串的主要操作与在顺序表中进行的相关操作的算法思想基本上是一致的,不同之处在于:
①因为动态顺序串的存储空间分配是以串的实际长度为标准的,所以在有些操作(如插入、连接等)中首先需要根据实际情况增补存储空间;
②在顺序串的操作中经常是针对串的整体(如串赋值)或串的一部分子串而进行的,这样的操作相对于顺序表要复杂一些。以串插入操作为例,首先需要增补存储空间以便能够存放插入后的串,其次是要把从插入位置pos到串末尾的每一个字符串向后移动T.length个位置(而不是顺序表的一个位置),最后再把子串中的每一个字符(而不是顺序表的一个字符)复制到主串的相应位置。所以他的时间开销相比顺序表的插入要多一些,其它操作类似。
